package com.ks.core.api;

import cn.hutool.crypto.digest.DigestUtil;
import com.ks.core.api.config.ApiConfig;
import com.ks.core.api.config.AuthProperties;
import com.ks.core.constant.Const;
import com.ks.core.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author kingdee
 */
@Component
@Slf4j
public class SignValidUtil {

    @Resource
    private AuthProperties properties;

    private ApiConfig apiConfig;

    public SignValidUtil(ApiConfig apiConfig) {
        this.apiConfig = apiConfig;
    }

    /**
     * success - 成功， 其他-失败信息
     *
     * @param request {@link ApiRequest}
     * @return 返回消息
     */
    public String check(ApiRequest request) {
        log.info("验证签名：BaseRequest=[{}]", JsonUtil.objectToJsonStr(request));
        Map<String, String> authPair = properties.getPair();
        if (CollectionUtils.isEmpty(authPair)) {
            return Const.SUCCESS_MESSAGE;
        }
        String appKey = request.getAppKey();
        String appSecret = authPair.get(appKey);
        if (appSecret == null) {
            return "appKey错误";
        }
        if (!validTimestamp(request.getTimestamp(), LocalDateTime.now())) {
            return "timestamp参数错误且时间范围为" + apiConfig.getTimeExpired() + "分钟内";
        }
        Map<String, String> param = JsonUtil.jsonStrToMap(JsonUtil.objectToJsonStr(request));
        param.put(ApiConfig.APP_SECRET_FIELD, appSecret);
        String sign = request.getSign();
        if (!verifySign(sign, param)) {
            return "签名[" + sign + "]错误";
        }
        return Const.SUCCESS_MESSAGE;
    }

    private boolean verifySign(String sign, Map<String, String> param) {
        String md5Sign = md5BuildParam(param);
        log.info("签名比较：sign=[{}], md5Sign=[{}]", sign, md5Sign);
        return sign.equals(md5Sign);
    }

    public static String md5BuildParam(Map<String, String> map) {
        Map<String, String> sortedMap = new TreeMap<>(map);
        String secret = map.get(ApiConfig.APP_SECRET_FIELD);
        StringBuilder sb = new StringBuilder(secret);
        for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
            String key = entry.getKey();
            if (ApiConfig.SIGN_FIELD.equalsIgnoreCase(key) || ApiConfig.APP_SECRET_FIELD.equalsIgnoreCase(key)) {
                continue;
            }
            sb.append(buildKeyValue(key, entry.getValue(), true));
        }
        sb.append(secret);
        return DigestUtil.md5Hex(sb.toString()).toUpperCase();
    }

    public static String buildKeyValue(String key, String value, boolean isEncode) {
        StringBuilder sb = new StringBuilder();
        sb.append(key);
        sb.append("=");
        if (isEncode) {
            try {
                sb.append(URLEncoder.encode(value, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                sb.append(value);
            }
        } else {
            sb.append(value);
        }
        return sb.toString();
    }

    private boolean validTimestamp(String timestamp, LocalDateTime now) {
        // 设置时区为东8区
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.systemDefault());
        try {
            LocalDateTime localDateTime = LocalDateTime.parse(timestamp, dateTimeFormatter);
            log.info("timestamp=[{}], now=[{}]", localDateTime, now);
            return Math.abs(Duration.between(localDateTime, now).toMinutes()) <= apiConfig.getTimeExpired();
        } catch (Exception e) {
            return false;
        }
    }
}
